/**
 * Alipay.com Inc. Copyright (c) 2004-2019 All Rights Reserved.
 */
package com.ruoyi.common.utils.qugongbao;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Base64;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

/**
 * SHA256WithRSA 签名器
 */
@Slf4j
public class Signer {

    public static final String SHA_256_WITH_RSA = "SHA256WithRSA";
    public static final String RSA = "RSA";
    public static final String SIGN_VALUE = "signValue";
    public static final String SIGN_TYPE_FIELD = "signType";


    /**
     * 对参数集合进行验签
     *
     * @param params    参数集合
     * @param publicKey 公钥
     * @return true：验证成功；false：验证失败
     */
    public static boolean verify(Map<String, String> params, String publicKey) {
        String signValue = params.get(SIGN_VALUE);
        params.remove(SIGN_TYPE_FIELD);
        params.remove(SIGN_VALUE);
        String sortString = createSortString(params);
        log.debug("sortString，content=" + sortString);
        String md5Str = SecureUtil.md5(sortString);
        log.debug("md5 content=" + md5Str);
        return verify(md5Str, signValue, publicKey);
    }


    /**
     * 验证签名
     *
     * @param content      待验签的内容
     * @param sign         签名值的Base64串
     * @param publicKeyPem 公钥
     * @return true：验证成功；false：验证失败
     */
    public static boolean verify(String content, String sign, String publicKeyPem) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            byte[] encodedKey = publicKeyPem.getBytes();
            encodedKey = Base64.decode(encodedKey);
            PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));

            Signature signature = Signature.getInstance(SHA_256_WITH_RSA);
            signature.initVerify(publicKey);
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            return signature.verify(Base64.decode(sign.getBytes()));
        } catch (Exception e) {
            String errorMessage = "验签遭遇异常，content=" + content + " sign=" + sign +
                    " publicKey=" + publicKeyPem + " reason=" + e.getMessage();
            throw new RuntimeException(errorMessage, e);
        }
    }

    /**
     * 对参数集合进行验签
     *
     * @param requestBean 参数bean
     * @param publicKey   公钥
     * @return true：验证成功；false：验证失败
     */
    public static String sign(ApiBaseRequestBean requestBean, String publicKey) {
        Map<String, String> params = new TreeMap<>();
        params.put("custNo", requestBean.getCustNo());
        params.put("body", requestBean.getBody());
        params.put("timestamp", requestBean.getTimestamp());
        params.put("v", requestBean.getV());
        params.put("reqNo", requestBean.getReqNo());
        String sortString = params.entrySet().stream()
                .filter(entity -> StrUtil.isNotBlank(entity.getKey()) && StrUtil.isNotBlank(entity.getValue()))
                .map(entry -> entry.getKey() + "=" + entry.getValue())
                .reduce((l, r) -> l + "&" + r)
                .orElse("");
        log.debug("sortString，content=" + sortString);
        String md5Str = SecureUtil.md5(sortString);
        log.debug("md5 content=" + md5Str);
        return sign(md5Str, publicKey);
    }

    /**
     * 对参数集合进行验签
     *
     * @param params    参数bean
     * @param publicKey 公钥
     * @return true：验证成功；false：验证失败
     */
    public static String sign(Map<String, String> params, String publicKey) {
        params.remove(SIGN_TYPE_FIELD);
        params.remove(SIGN_VALUE);
        String sortString = createSortString(params);
        log.debug("sortString，content=" + sortString);
        String md5Str = SecureUtil.md5(sortString);
        log.debug("MD5签名源串，content=" + md5Str);
        return sign(md5Str, publicKey);
    }

    /**
     * 计算签名
     *
     * @param content       待签名的内容
     * @param privateKeyPem 私钥
     * @return 签名值的Base64串
     */
    public static String sign(String content, String privateKeyPem) {
        try {
            byte[] encodedKey = Base64.decode(privateKeyPem.getBytes());
            PrivateKey privateKey = KeyFactory.getInstance(RSA).generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
            Signature signature = Signature.getInstance(SHA_256_WITH_RSA);
            signature.initSign(privateKey);
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            byte[] signed = signature.sign();
            return new String(Base64.encode(signed));
        } catch (Exception e) {
            String errorMessage = "签名遭遇异常，content=" + content + " privateKeySize=" + privateKeyPem.length() + " reason=" + e.getMessage();
            throw new RuntimeException(errorMessage, e);
        }
    }

    public static String createSortString(Map<String, String> params) {
        String sortString = "";
        if (CollectionUtil.isNotEmpty(params)) {
            sortString = params.entrySet().stream()
                    .filter(entity -> StrUtil.isNotBlank(entity.getKey()) && StrUtil.isNotBlank(entity.getValue()))
                    .sorted(Comparator.comparing(Map.Entry::getKey))
                    .map(entry -> entry.getKey() + "=" + entry.getValue())
                    .reduce((l, r) -> l + "&" + r)
                    .orElse("");
        }
        return sortString;
    }


    /**
     * 生成 rsa 密钥对
     *
     * @param keySize
     */
    public static KeyPair generatorRsaKey(Integer keySize) {
        if (keySize == null) {
            keySize = 2048;
        }
        return SecureUtil.generateKeyPair(RSA, keySize);
    }

    public static String generatorAesKey(Integer keySize) {
        if (keySize == null) {
            keySize = 128;
        }
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue(), keySize).getEncoded();
        return java.util.Base64.getEncoder().encodeToString(key);
    }


    public static void main(String[] args) {
        //generator(2048);
    	//计算余数
    	double yu = new BigDecimal(1500.82).divideAndRemainder(new BigDecimal(300))[1].setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    	//计算商数
    	int shang = new BigDecimal(1000.08).divide(new BigDecimal(3)).intValue();
    	System.out.println("shang="+shang);
    	System.out.println("yu="+yu);
        generatorRsaKey(2048);
    }

}